<?php
namespace App\Lib\WeApp\Api;

use App\Jobs\Mp\PublishGetStatusJob;
use App\Lib\Uploads\UploadHandler;
use App\Lib\WeApp\Support\Encryptor;
use App\Lib\WeApp\Support\Message;
use App\Lib\WeApp\Support\Traits\DecryptXmlMessageTrait;
use App\Lib\WeApp\Support\Traits\InteractWithHandlersTrait;
use App\Lib\WeApp\Support\Traits\RespondXmlMessageTrait;
use App\Models\Mp\WxMpFreePublish;
use App\Wen\Utils\StrUtils;
use Closure;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
use Illuminate\Http\Response;
use Psr\Http\Message\ResponseInterface;

class MpServicer extends BaseApi{

    use InteractWithHandlersTrait;
    use DecryptXmlMessageTrait;
    use RespondXmlMessageTrait;

    protected ?Encryptor $encryptor = null;
    protected $aes_key;
    protected $token;

    public function __construct($appid,$secret, $token = '', $aes_key = '') {
        parent::__construct($appid,$secret, 'mp');
        $this->token = $token;
        $this->aes_key = $aes_key;
        $this->encryptor = new Encryptor($appid, $token, $aes_key);
    }

    public function getJsApiTicket($key, $param, $ttl = 7100){
        return Cache::remember($key, $ttl, function () use (&$param){
            $url = ApiUrl::MP_SERVICER_JSSDK_TICKET;

            $data = $this->sendRequestWithToken($url,$param, false);
            if($data && $data['errcode'] == 0 && $data['ticket']){
                return $data['ticket'];
            }else{
                _logger_(__file__, __line__, $data);
                return null;
            }
        });
    }



    public function getJsSdkConfig($url)
    {
        $param = array(
            'type' => 'jsapi'
        );
        $ticket = $this->getJsApiTicket('mpservicer:jsapi:ticket', $param, 7100);

        $nonceStr = Str::random(16);
        $timestamp = time();
        $string = "jsapi_ticket={$ticket}&noncestr={$nonceStr}&timestamp={$timestamp}&url={$url}";

        $signature = sha1($string);

        return [
            'jsapi_ticket' => $ticket,
            'appId' => $this->appid,
            'nonceStr' => $nonceStr,
            'timestamp' => $timestamp,
            'url' => $url,
            'signature' => $signature,
            'rawString' => $string
        ];
    }

    public function SyncCustomMenu($array){
        if(_empty_($array)){
            return [
                'code' => -1,
                'msg' => '数据为空'
            ];
        }
        $url = ApiUrl::MP_SERVICER_MENU_CREATE;
        $param = [
            'button' => $array
        ];
        $data = $this->sendRequestWithToken($url,$param);
        if($data){
            if($data['errcode'] == 0){
                return [
                    'code' => 200,
                    'msg' => 'ok'
                ];
            }else{
                //40028:invalid menu api user rid
                if($data['errcode'] == 40028 && StrUtils::startsWith($data['errmsg'], 'invalid menu api')){
                    throw new \Exception('该公众号未开启自定义菜单配置');
                }
                return [
                    'code' => $data['errcode'],
                    'msg' => $data['errmsg']
                ];
            }
        }
        return [
            'code' => -1,
            'msg' => $data
        ];
    }

    public function freePublishSubmit($free_publish_id){
        if(_empty_($free_publish_id)){
            return [
                'code' => -1,
                'msg' => ''
            ];
        }
        $free_publish = WxMpFreePublish::find($free_publish_id);
        if(!$free_publish){
            return [
                'code' => -1,
                'msg' => '不存在'
            ];
        }
        if($free_publish->parent_id > 0){
            return [
                'code' => -1,
                'msg' => '请勿点击子级'
            ];
        }

        // 未提交草稿 || 草稿需更新
        if(_empty_($free_publish->media_id) || $free_publish->updated_at > $free_publish->add_draft_time){
            $res = $this->addOrUpdateDraft($free_publish_id);
            if($res['code'] == 200){
                $free_publish = WxMpFreePublish::find($free_publish_id);
            }else{
                return [
                    'code' => $res['code'],
                    'msg' => $res['msg']
                ];
            }
        }

        if(_empty_($free_publish->media_id)){
            return [
                'code' => -1,
                'msg' => '错误'
            ];
        }

        // 0: 错误 1：新增 2：修改
        $flag = 0;
        if(_empty_($free_publish->article_id)){
            if(_empty_($free_publish->publish_id)){
                $flag = 1;
            }else{
                // 发布中
                if($free_publish->publish_status == 1){
                    return [
                        'code' => -1,
                        'msg' => '已经在发布中，请耐心等待微信发布'
                    ];
                }else{
                    $flag = 1;
                }
            }
            if($flag){

            }
        }else{
            // 修改
            $flag = 2;
        }

        if($flag == 1){
            $url = ApiUrl::MP_SERVICER_FREEPUBLISH_SUBMIT;
            $res = $this->sendRequestWithToken($url,[
                'media_id' => $free_publish->media_id
            ]);
            if($res && _array_key($res, 'errcode', -1) != -1){
                if($res['errcode'] === 0){
                    WxMpFreePublish::where('id', $free_publish->id)->update( ['publish_id'=>$res['publish_id']] );
                    PublishGetStatusJob::dispatch($res['publish_id']);
                    return [
                        'code' => 200,
                        'msg' => 'ok',
                        'publish_id' => $res['publish_id']
                    ];
                }else{
                    return [
                        'code' => $res['errcode'],
                        'msg' => $res['errmsg'],
                    ];
                }
            }else{
                _logger_(__file__, __line__, $res);
                return [
                    'code' => -1,
                    'msg' => '返回错误'
                ];
            }
        }else if($flag == 2){
            return [
                'code' => -1,
                'msg' => '已发布内部不允许修改'
            ];
        }

    }


    public function deleteDraft($media_id){
        $url = ApiUrl::MP_SERVICER_FREEPUBLISH_DRAFT_DELETE;
        $res = $this->sendRequestWithToken($url,[
            'media_id' => $media_id
        ]);
        if($res && _array_key($res, 'errcode', -1) != -1){
            if($res['errcode'] === 0){
                return [
                    'code' => 200,
                    'msg' => 'ok',
                ];
            }else{
                return [
                    'code' => $res['errcode'],
                    'msg' => $res['errmsg'],
                ];
            }
        }else{
            _logger_(__file__, __line__, $res);
            return [
                'code' => -1,
                'msg' => '返回错误'
            ];
        }
    }

    public function deleteFreePublish($article_id, $index = 0){
        $url = ApiUrl::MP_SERVICER_FREEPUBLISH_DELETE;
        $res = $this->sendRequestWithToken($url,[
            'article_id' => $article_id,
            'index' => $index
        ]);
        if($res && _array_key($res, 'errcode', -1) != -1){
            if($res['errcode'] === 0){
                return [
                    'code' => 200,
                    'msg' => 'ok',
                ];
            }else{
                return [
                    'code' => $res['errcode'],
                    'msg' => $res['errmsg'],
                ];
            }
        }else{
            _logger_(__file__, __line__, $res);
            return [
                'code' => -1,
                'msg' => '返回错误'
            ];
        }
    }

    public function addOrUpdateDraft($free_publish_id){
        if(_empty_($free_publish_id)){
            return [
                'code' => -1,
                'msg' => ''
            ];
        }
        $free_publish = WxMpFreePublish::find($free_publish_id);
        if(!$free_publish){
            return [
                'code' => -1,
                'msg' => '不存在'
            ];
        }
        $update_index = 0;
        $update_publish_id = $free_publish_id;
        if($free_publish->parent_id > 0){
            $pluck_ids = WxMpFreePublish::where('parent_id', $free_publish->parent_id)->orderBy('order', 'asc')->orderBy('id', 'asc')->pluck('id')->toArray();
            if($pluck_ids){
                $update_index = array_search($free_publish->id, $pluck_ids) + 1;
            }else{
                return [
                    'code' => -1,
                    'msg' => '错误'
                ];
            }
            $free_publish_id = $free_publish->parent_id;
            $free_publish = WxMpFreePublish::find($free_publish_id);
            if($free_publish->parent_id > 0){
                return [
                    'code' => -1,
                    'msg' => '层级错误'
                ];
            }
        }

        if(!_empty_($free_publish->media_id)){
            $url = ApiUrl::MP_SERVICER_FREEPUBLISH_DRAFT_UPDATE;
            $para_arr = [
                'media_id' => $free_publish->media_id,
                'index' => $update_index,
                'articles' => WxMpFreePublish::find($update_publish_id)->normal()
            ];
        }else{
            $url = ApiUrl::MP_SERVICER_FREEPUBLISH_DRAFT_ADD;
            $para_arr = [
                'articles' => []
            ];
            if($free_publish){
                $para_arr['articles'][] = $free_publish->normal();
                WxMpFreePublish::where('parent_id', $free_publish->id)->orderBy('order', 'asc')->orderBy('id', 'asc')->get()->map(function ($v) use (&$para_arr){
                    $para_arr['articles'][] = $v->normal();
                });
            }
            if(_empty_($para_arr['articles'])){
                return [
                    'code' => -1,
                    'msg' => '一个内容也没有'
                ];
            }
        }
        try {
            $res = $this->sendRequestWithToken($url,$para_arr);
            if(_empty_($free_publish->media_id)){
                if($res && _array_key($res, 'media_id', '')){
                    WxMpFreePublish::where('id', $free_publish->id)->update( ['media_id'=>$res['media_id'], 'add_draft_time'=>current_time()] );
                    return [
                        'code' => 200,
                        'media_id' => $res['media_id']
                    ];
                }else if($res && _array_key($res, 'errcode', -1) != -1){
                    return [
                        'code' => $res['errcode'],
                        'msg' => $res['errmsg']
                    ];
                }else{
                    return [
                        'code' => -1,
                        'msg' => ''
                    ];
                }
            }else{
                if($res && _array_key($res, 'errcode', -1) != -1){
                    if(_array_key($res, 'errcode', -1) === 0){
                        WxMpFreePublish::where('id', $free_publish->id)->update( ['add_draft_time'=>current_time()] );
                        return [
                            'code' => 200,
                            'msg' => 'ok'
                        ];
                    }else{
                        return [
                            'code' => $res['errcode'],
                            'msg' => $res['errmsg']
                        ];
                    }
                }else{
                    return [
                        'code' => -1,
                        'msg' => ''
                    ];
                }
            }
        }catch (\Exception $e){
            _logger_(__file__, __line__, $e->getMessage());
            return [
                'code' => -1,
                'msg' => $e->getMessage()
            ];
        }


    }

    public function getArticle($article_id){
        if(_empty_($article_id)){
            return [
                'code' => -1,
                'msg' => ''
            ];
        }
        $url = ApiUrl::MP_SERVICER_GET_GET_ARTICLE;
        $res = $this->sendRequestWithToken($url,[
            "article_id" => $article_id,
        ]);
        if($res && _array_key($res, 'news_item', [])){
            return [
                'code' => 200,
                'type' => 'mpnews',
                'news_item' => $res['news_item'],
            ];
        }else if($res && _array_key($res, 'errcode', -1) != -1){
            // {"errcode":40007,"errmsg":"invalid media_id"}
            return [
                'code' => $res['errcode'],
                'msg' => $res['errmsg']
            ];
        }else{
            return [
                'code' => -1,
                'msg' => ''
            ];
        }
    }

    public function getUserInfo($open_id){
        if(_empty_($open_id)){
            return [
                'code' => -1,
                'msg' => ''
            ];
        }
        $url = ApiUrl::MP_SERVICER_USER_INFO;
        $res = $this->sendRequestWithToken($url,[
            "openid" => $open_id,
        ], false);
        if($res && _array_key($res, 'subscribe', 0) > 0){
            // https://developers.weixin.qq.com/doc/offiaccount/User_Management/Get_users_basic_information_UnionID.html#UinonId
            return [
                'code' => 200,
                'unionid' => $res['unionid'],
                'openid' => $res['openid'],
            ];
        }else if($res && _array_key($res, 'errcode', -1) != -1){
            // {"errcode":40007,"errmsg":"invalid media_id"}
            return [
                'code' => $res['errcode'],
                'msg' => $res['errmsg']
            ];
        }else{
            return [
                'code' => -1,
                'msg' => ''
            ];
        }
    }


    public function getMaterial($media_id){
        if(_empty_($media_id)){
            return [
                'code' => -1,
                'msg' => ''
            ];
        }
        $url = ApiUrl::MP_SERVICER_GET_MATERIAL;
        $res = $this->sendRequestWithToken($url,[
            "media_id" => $media_id,
        ]);

        if(is_array($res)){
            if($res && _array_key($res, 'errcode', -1) && $res['errcode'] !== 0){
                // {"errcode":40007,"errmsg":"invalid media_id"}
                return [
                    'code' => $res['errcode'],
                    'msg' => $res['errmsg']
                ];
            }else if($res){
                if(_array_key($res, 'news_item', [])){
                    // 图文素材
                    return [
                        'code' => 200,
                        'type' => 'mpnews',
                        'news_item' => $res['news_item'],
                    ];
                }else if(_array_key($res, 'down_url', '') && _array_key($res, 'title', '')){
                    // 视频素材
                    return [
                        'code' => 200,
                        'type' => 'video',
                        'title' => $res['title'],
                        'description' => $res['description'],
                        'down_url' => $res['down_url'],
                    ];
                }
            }else{
                return [
                    'code' => -1,
                    'msg' => ''
                ];
            }
        }else if(is_string($res)){
            if(@getimagesizefromstring($res) !== false){
                // 是图片
                $upload_res = UploadHandler::binary($res);
                if($upload_res && _array_key($upload_res, 'url', '')){
                    return [
                        'code' => 200,
                        'type' => 'image',
                        'url' => $upload_res['url'],
                    ];
                }
            }
        }
    }


    public function getFreepublishBatchget(){
        $url = ApiUrl::MP_SERVICER_FREEPUBLISH_BATCHGET;
        $res = $this->sendRequestWithToken($url,[
            "offset" => 0,
            "count" => 20,
            "no_content" => 1
        ]);
        if($res && _array_key($res, 'total_count', 0) > 0){
            return [
                'code' => 200,
                'item' => $res['item'],
                'total_count' => $res['total_count'],
                'item_count' => $res['item_count']
            ];
        }else if($res && _array_key($res, 'errcode', -1) != -1){
            // {"errcode":40007,"errmsg":"invalid media_id"}
            return [
                'code' => $res['errcode'],
                'msg' => $res['errmsg']
            ];
        }else{
            return [
                'code' => -1,
                'msg' => ''
            ];
        }
    }


    public function getPublishStatus($publish_id){
        if(_empty_($publish_id)){
            return [
                'code' => 200,
                'msg' => 'ok'
            ];
        }
        $url = ApiUrl::MP_SERVICER_FREEPUBLISH_GET;
        $res = $this->sendRequestWithToken($url,[
            "publish_id" => $publish_id,
        ]);
        if($res && _array_key($res, 'publish_id', '')){
            $article_url = '';
            $article_detail = _array_key($res, 'article_detail', []);
            if(!_empty_($article_detail)){
                $article_item = _array_key($article_detail, 'item', []);
                if(!_empty_($article_item)){
                    $item_0 = _array_key($article_item, 0, []);
                    if(!_empty_($item_0)){
                        $article_url = _array_key($item_0, 'article_url', '');
                    }
                }
            }
            return [
                'code' => 200,
                'publish_status' => $res['publish_status'],
                'article_id' => _array_key($res, 'article_id', ''),
                'article_url' => $article_url
            ];
        }else if($res && _array_key($res, 'errcode', -1) != -1){
            // {"errcode":40007,"errmsg":"invalid media_id"}
            return [
                'code' => $res['errcode'],
                'msg' => $res['errmsg']
            ];
        }else{
            return [
                'code' => -1,
                'msg' => ''
            ];
        }
    }

    /**
     * @param $media_id 删除素材id
     * @return array
     */
    public function deleteMaterial($media_id){
        $url = ApiUrl::MP_SERVICER_DEL_MATERIAL;
        $res = $this->sendRequestWithToken($url,[
            "media_id" => $media_id,
        ]);
        _logger_(__file__, __line__, $res);
        if($res && _array_key($res, 'errcode', -1) != -1){
            if($res['errcode'] === 0){
                return [
                    'code' => 200,
                    'msg' => 'ok'
                ];
            }else{
                return [
                    'code' => $res['errcode'],
                    'msg' => $res['errmsg']
                ];
            }
        }
        return [
            'code' => -1,
            'msg' => ''
        ];
    }

    public function uploadMediaImg($media_url){
        if(_empty_($media_url)){
            return [
                'code' => -1,
                'msg' => ''
            ];
        }
        $url = ApiUrl::MP_SERVICER_MEDIA_UPLOADIMG;
        $accessToken = $this->getAccessToken();
        $response = Http::get($media_url, ['stream' => true]);
        if ($response->failed()) {
            throw new \Exception('Failed to download image');
        }

        // 获取文件信息
        $filename = basename($media_url);
        $fileSize = $response->header('Content-Length');
        $fileType = $response->header('Content-Type');

        // 上传到微信
        $uploadResponse = Http::attach(
            'media',
            $response->body(),
            $filename
        )->post($url."?access_token={$accessToken}");

        if ($uploadResponse->failed()) {
            throw new \Exception('Failed to upload image to WeChat: ' . $uploadResponse->body());
        }
        $res = $uploadResponse->json();
        if($res && _array_key($res, 'url', '')){
            return [
                'code' => 200,
                'url' => _array_key($res, 'url', ''),
            ];
        }else if($res && _array_key($res, 'errcode', -1) != -1){
            // {"errcode":40007,"errmsg":"invalid media_id"}
            return [
                'code' => $res['errcode'],
                'msg' => $res['errmsg']
            ];
        }else{
            return [
                'code' => -1,
                'msg' => ''
            ];
        }
    }

    /*
     * 媒体文件类型，分别有图片（image）、语音（voice）、视频（video）和缩略图（thumb）
     */
    public function uploadMaterial($media_url, $type = 'image', $title = '', $introduction = ''){
        if(_empty_($media_url) || !StrUtils::startsWith($media_url, 'http')){
            return [
                'code' => -1,
                'msg' => ''
            ];
        }
        $url = ApiUrl::MP_SERVICER_ADD_MATERIAL;
        $accessToken = $this->getAccessToken();
        if($type == 'image' || $type = 'voice' || $type == 'video'){
            $response = Http::get($media_url, ['stream' => true]);
            if ($response->failed()) {
                throw new \Exception('Failed to download image');
            }

            // 获取文件信息
            $filename = basename($media_url);
            $fileSize = $response->header('Content-Length');
            $fileType = $response->header('Content-Type');

            // 上传到微信
            if($type == 'video'){
                $uploadResponse = Http::attach(
                    'media',
                    $response->body(),
                    $filename
                )->attach(
                    'description', json_encode([
                        'title' => $title,
                        'introduction' => $introduction
                    ], JSON_UNESCAPED_UNICODE)
                )->post($url."?access_token={$accessToken}&type={$type}");

            }else{
                $uploadResponse = Http::attach(
                    'media',
                    $response->body(),
                    $filename
                )->post($url."?access_token={$accessToken}&type={$type}");
            }

            if ($uploadResponse->failed()) {
                throw new \Exception('Failed to upload image to WeChat: ' . $uploadResponse->body());
            }
            $res = $uploadResponse->json();
            //[
            //  "media_id" => "vc42HYzz5iq5GPi5xKD8Kp4ODLav00JBm1oldrRILLPwOrNWCMcfD4wDKQAr3oWS"
            //  "url" => "http://mmbiz.qpic.cn/sz_mmbiz_png/MTfRQsO8tpWpBdru17LiaZ371icLNCPXfDhRzSAx92jlNdkyqicFMgiaQdWia542CGBYmN5AjCkH931TxIQldv8mwuQ/0?wx_fmt=png"
            //  "item" => []
            //]
            if($res && _array_key($res, 'media_id', '')){
                return [
                    'code' => 200,
                    'media_id' => $res['media_id'],
                    'url' => _array_key($res, 'url', ''),
                ];
            }else if($res && _array_key($res, 'errcode', -1) != -1){
                // {"errcode":40007,"errmsg":"invalid media_id"}
                return [
                    'code' => $res['errcode'],
                    'msg' => $res['errmsg']
                ];
            }else{
                return [
                    'code' => -1,
                    'msg' => ''
                ];
            }
        }

    }



    public function getQrCodeWithPara($scene_id, $scene_str, $temporary = true){
        if($temporary){
            $temporaryQrCodeData = [
                'expire_seconds' => 60 * 60 * 24, // 二维码有效时间，单位为秒
                'action_name' => $scene_id > 0 ? 'QR_SCENE' : 'QR_STR_SCENE', // 二维码类型，临时二维码 永久：QR_LIMIT_SCENE为永久的整型参数值，QR_LIMIT_STR_SCENE
                'action_info' => [
                    'scene' => [
                        'scene_id' => $scene_id, // 场景值 ID
                        'scene_str' => $scene_str
                    ]
                ]
            ];
            $data = $this->sendRequestWithToken(ApiUrl::MP_SERVICER_WITHPARA_QRCODE_CREATE, $temporaryQrCodeData, true);
            if($data && _array_key($data, 'url', '') ){
                return $data['url'];
            }else{
                _logger_(__file__, __line__, $data);
                return null;
            }
        }
    }

    // 下面是公众号消息处理
    public function response($request){
        // 公众号后台配置时，起验证作用
        if ((bool) ($str = $request->input('echostr') ?? '')) {
            return new Response($str, 200, []);
        }
        $message = Message::createFromRequest($request);
        $query = $request->all();
        if ($this->encryptor && ! empty($query['msg_signature'])) {
            $this->prepend($this->decryptRequestMessage($query));
        }

        $response = $this->handle(new Response('success',200, []), $message);
        if (! ($response instanceof ResponseInterface)) {
            try {
                $response = $this->transformToReply($response, $message, $this->encryptor);
            }catch (\Exception $e){
                return $response;
            }
        }
        return $response;
    }

    protected function decryptRequestMessage(array $query): Closure
    {
        return function (Message $message, Closure $next) use ($query): mixed {
            if (! $this->encryptor) {
                return null;
            }

            if(_empty_($query['msg_signature'] ?? '')){
                return $next($message);
            }

            $this->decryptMessage($message, $this->encryptor, $query['msg_signature'] ?? '', $query['timestamp'] ?? '', $query['nonce'] ?? '');

            return $next($message);
        };
    }
    /**
     */
    public function getDecryptedMessage($request)
    {
        $message = Message::createFromRequest($request);
        $query = $request->all();

        if (! $this->encryptor || empty($query['msg_signature'])) {
            return $message;
        }

        return $this->decryptMessage($message, $this->encryptor, $query['msg_signature'] ?? '', $query['timestamp'] ?? '', $query['nonce'] ?? ''
        );
    }
}
